package ocbn.remotelab.xml;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Vector;
import net.rim.device.api.xml.parsers.SAXParser;
import net.rim.device.api.xml.parsers.SAXParserFactory;
import net.rim.device.api.xml.parsers.ParserConfigurationException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ocbn.remotelab.events.*;
import ocbn.remotelab.events.MassSpectrometerStateEvent.State;
import ocbn.remotelab.exceptions.DateFormatException;
import ocbn.remotelab.util.NumberConverter;
import ocbn.remotelab.util.date.Date;

/**
 * A class that imports XML and converts it to a Vector of Events. It uses SAX
 * to parse XML. 
 *
 * @author Ke Li
 */
public class XMLImport {

    /**
     * The XML SAX parser used by this class
     */

    private static SAXParser parser;

    /**
     * Static initializer that gets a new instance of SAX parser and assigns it
     * to the parser static variable
     */

    static {
        try {
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            parserFactory.setValidating(false);
            parserFactory.setAllowUndefinedNamespaces(true);
            // Get a new instance of SAX parser from SAX Parser Factory
            parser = parserFactory.newSAXParser();
        } catch (ParserConfigurationException e) {
            System.err.println("XML Parser Configuration Error");
        }  catch (SAXException e) {
            System.err.println("XML Parsing Error");
        }
    }

    /**
     * Imports XML and converts it to a Vector of Events.
     *
     * @param XML   A String containing XML representation of Events
     * @return      A Vector of Events
     */

    public Vector importXML(String XML) {

        try {
            XMLToEventsConverter converter = new XMLToEventsConverter();
            // Register the converter as the event handler and parse the XML
            parser.parse(new ByteArrayInputStream(XML.getBytes()), converter);
            return converter.getOutput();
        } catch (SAXException e) {
            System.err.println("XML Parsing Error");
        } catch (IOException e){
            System.err.println("Input/Output Stream Error");
        }
        // Return an empty Vector if an exception occured
        return new Vector();
    }

    /**
     * Imports a Vector of XML documents and converts them to a Vector of Events
     *
     * @param XMLdocs   A Vector containing many XML documents each representing
     *                  one or more Events
     * @return          A Vector of Events
     */

    public Vector importXML(Vector XMLdocs) {

        Vector allEvents = new Vector();
        Vector tempEvents;
        for (int i = 0; i < XMLdocs.size(); i++) {
            tempEvents = importXML((String)XMLdocs.elementAt(i));
            for (int j = 0; j < tempEvents.size(); j++) {
                allEvents.addElement(tempEvents.elementAt(j));
            }
        }
        return allEvents;

    }
    
    /**
     * A private inner class that handles the events generated by the SAX parser
     * and converts the XML to a Vector of Events
     */

    private class XMLToEventsConverter extends DefaultHandler {

        /**
         * The text inside the current element. For example, in
         * &lt;tag&gt;text&lt;/tag&gt;, "text" would be stored in this variable.
         */

        private String tempVal;

        /**
         * The event that is currently being imported
         */

        private Event tempEvent;

        /**
         * The output of this converter as a Vector of Events
         */

        private Vector output = new Vector();

        /**
         * This method is called when the parser encounters the start of an
         * element.
         * 
         * @param uri           A String representing the URI of the element
         * @param localName     A String representing the local name
         * @param qName         A String representing the qualified name
         * @param attributes    An Attributes object representing the attributes
         *                      of the element
         * @throws SAXException Indicates that an error occured when parsing the
         *                      XML
         */
        
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {
            // Reset temporary value
            tempVal = "";
            if (qName.equalsIgnoreCase("event")) {
                if (attributes.getValue("type").equalsIgnoreCase("MS State")) {
                    tempEvent = new MassSpectrometerStateEvent();
                }
            }
        }

        /**
         * This method is called when the parser encounters text inside an
         * element. 
         *
         * @param ch            A character array representing the text inside
         *                      the element
         * @param start         The starting position of the text
         * @param length        The length of the text
         * @throws SAXException Indicates that an error occured when parsing the
         *                      XML
         */

        public void characters(char[] ch, int start, int length)
                throws SAXException {
            tempVal += new String(ch, start, length);
        }

        /**
         * This method is called when the parser encounters the end of an
         * element. 
         *
         * @param uri           A String representing the URI of the element
         * @param localName     A String representing the local name
         * @param qName         A String representing the qualified name
         * @throws SAXException Indicates that an error occured when parsing the
         *                      XML
         */

        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            if (qName.equalsIgnoreCase("event")) {
                // Add the current event to the Vector of Events
                                                                                // TO DO: validate
                output.addElement(tempEvent);
            } else if (qName.equalsIgnoreCase("id")) {
                // Set the ID of current event
                tempEvent.setID(NumberConverter.toLong(tempVal));
            } else if (qName.equalsIgnoreCase("time")) {
                // Set the time of current event
                try{
                    tempEvent.setTime(Date.valueOf(tempVal));
                }catch(DateFormatException e){
                    tempEvent.setTime(new Date());
                }
            } else if (qName.equalsIgnoreCase("state")) {
                // If the current event is a mass spectrometer state event,
                if(tempEvent.getType() == MassSpectrometerStateEvent.class){
                    // Set the mass spectrometer state in the event
                    ((MassSpectrometerStateEvent) tempEvent).setState
                            ((State) State.valueOf(tempVal.toUpperCase()));
                }
            } else if (qName.equalsIgnoreCase("source")) {
                if(tempEvent.getType() == MassSpectrometerStateEvent.class){
                    // Set the source of the event
                    ((MassSpectrometerStateEvent)tempEvent).setSource(tempVal);
                }
            } else if (qName.equalsIgnoreCase("details")) {
                if(tempEvent.getType() == MassSpectrometerStateEvent.class){
                    // Set the details of the event
                    ((MassSpectrometerStateEvent)tempEvent).setDetails(tempVal);
                }
            }
        }

        /**
         * Gets the output as a Vector of Events.
         *
         * @return  A Vector of Events that is converted from XML
         */

        public Vector getOutput(){
            return output;
        }
    }
}